<template>
  <div class="snap">
    <el-scrollbar style="height: 100%">
      <svg id="svg">
        <!-- <g style="font-size: 14pt;">
        <path d="M 100 10 100 100" style="stroke: gray; fill: none;" />
        <text x="100" y="30" style="text-anchor: start">Start</text>
        <text x="100" y="60" style="text-anchor: middle">Middle</text>
        <text x="100" y="90" style="text-anchor: end">End</text>
        </g>-->
        <!-- <line x1="0" y1="5" x2="100" y2="5" stroke-dasharray="2 2" stroke="red"/> -->
      </svg>
    </el-scrollbar>
    <!-- <div class="toolbar">
      <a @click="scaleChange('up')">+</a>
      <a @click="scaleChange('down')">-</a>
    </div> -->
  </div>
</template>

<script>
import Snap from 'snapsvg'
import { getObjectKeys } from "./config.js"
export default {
  components: {},
  name: "znxdLinkLeftRight",
  data () {
    return {
      // 实时参数
      startX: 0,
      startY: 0,
      currentElement: null,
      // 全局参数
      svg: null,
      g: null,
      scale: 1,
      // 可配置参数
      rectConfig: {
        width: 460,
        height: 40
      },
      interval: {
        x: 200,
        y: 0
      },
      linkData: [] // 返回值
    }
  },
  props: {
    // 数据节点 title  header(设置字段之后，普通列表可以根据header字段设置进行配置) 其他对象id为必须项并且必须符合html规范
    // nodeData: {
    //   dataFrom: [
    //     {
    //       mode: "title",
    //       label: "源表字段"
    //     },
    //     {
    //       mode: "header",
    //       columnName: "字段名称",
    //       columnType: "字段类型",
    //       columnLong: "字段长度"
    //     },
    //     {
    //       id: "from-a",
    //       columnName: "a",
    //       columnType: "int",
    //       columnLong: 10
    //     },
    //     {
    //       id: "from-b",
    //       columnName: "b",
    //       columnType: "int",
    //       columnLong: 10
    //     },
    //   ],
    //   dataTo: [
    //     {
    //       mode: "title",
    //       label: "目标表字段"
    //     },
    //     {
    //       mode: "header",
    //       columnName: "字段名称",
    //       columnType: "字段类型",
    //       columnLong: "字段长度"
    //     },
    //     {
    //       id: "to-1",
    //       columnName: "1",
    //       columnType: "int",
    //       columnLong: 10
    //     },
    //     { id: "to-2", columnName: "2", columnType: "int", columnLong: 10 },
    //   ]
    nodeData: Object,
    // 链接关系
    // linkData: [
    //   { from: "from-a", to: "to-1" },
    //   { from: "from-b", to: "to-2" },
    //   { from: "from-c", to: "to-3" }
    // ],
    initData: Array
  },
  computed: {},
  watch: {
    linkData: {
      deep: true,
      handler: function (val) {
        this.$emit("input", val)
      }
    },
    nodeData: {
      deep: true,
      handler: function (val) {
        this.initSVG()
      }
    },
    initData: {
      deep: true,
      handler: function (val) {
        if (val) { this.linkData = [...val] }
        this.initSVG()
      }
    }
  },
  created: function () { },
  methods: {
    initSVG () {
      if (this.svg !== null) { this.svg.clear() }
      this.svg = Snap("#svg")
      this.g = this.svg.g()
      this.g.attr("id", "svgBox")
      let dataCount = this.nodeData.dataFrom.length
      if (dataCount < this.nodeData.dataTo.length) {
        dataCount = this.nodeData.dataTo.length
      }
      let svg_height = 40 * dataCount * this.scale
      this.svg.attr({
        height: svg_height > 520 ? svg_height : 520
      })
      // this.g.drag(this.dragMove, this.dragStart, this.dragEnd);
      this.initNode(this.g, this.nodeData) // 加载节点
      this.initLinks(this.linkData) // 加载连线
    },
    initNode (parentNode, nodes) {
      let _this = this
      let parentMatrix = parentNode.transform().totalMatrix // 获取父节点坐标，以父节点坐标为参照画图
      let headerColumn = []
      nodes.dataFrom.map((node, index) => {
        let _x = parentMatrix.e / parentMatrix.a
        let _y = parentMatrix.f / parentMatrix.a

        let x_value = _x // 设置当前节点x-value
        let y_value = _y + index * (this.rectConfig.height + this.interval.y) // 设置当前节点y-value
        switch (node.mode) {
          case "title":
            {
              let rect = _this.svg.rect(
                0, 0, _this.rectConfig.width, _this.rectConfig.height, 0, 0
              )
              rect.attr("style", "fill:transparent;   ")
              // 矩形文字
              let text = _this.svg.text(
                0, (_this.rectConfig.height + 10) / 2, node.label
              )
              text.attr("style", "fill:#2D8CF0;")
              let _g = _this.svg.g()
              _g.add(rect, text)
              _g.attr(
                "transform", "translate(" + x_value + "," + y_value + ")"
              )
            }
            break
          case "header":
            {
              let _g = _this.svg.g()
              let keys = getObjectKeys(node)
              headerColumn = keys.filter((key) => {
                return key !== "mode"
              })
              headerColumn.map((key, keyIndex) => {
                let rect = _this.svg.rect(
                  (keyIndex * _this.rectConfig.width) / headerColumn.length, 0, _this.rectConfig.width / headerColumn.length, _this.rectConfig.height, 0, 0
                )
                rect.attr(
                  "style", "fill:#EDEDED; stroke:#EDEDED; stroke-width:0.5  "
                )
                // 矩形文字
                let text = _this.svg.text(
                  (keyIndex * _this.rectConfig.width) / headerColumn.length +
                  _this.rectConfig.width / headerColumn.length / 2, (_this.rectConfig.height + 10) / 2, node[key]
                )
                text.attr(
                  "style", "dominant-baseline:middle;text-anchor:middle;"
                )
                _g.add(rect, text)
              })
              _g.attr(
                "transform", "translate(" + x_value + "," + y_value + ")"
              )
            }

            break
          default:
            {
              let _g = this.svg.g()
              headerColumn.map((key, keyIndex) => {
                let rect = _this.svg.rect(
                  (keyIndex * _this.rectConfig.width) / headerColumn.length, 0, _this.rectConfig.width / headerColumn.length, _this.rectConfig.height, 0, 0
                )
                rect.attr(
                  "style", "fill:transparent; stroke:#EDEDED; stroke-width:0.5  "
                )
                // 矩形文字
                let text = _this.svg.text(
                  (keyIndex * _this.rectConfig.width) / headerColumn.length +
                  _this.rectConfig.width / headerColumn.length / 2, (_this.rectConfig.height + 10) / 2, node[key]
                )
                text.attr(
                  "style", "dominant-baseline:middle;text-anchor:middle;"
                )
                _g.add(rect, text)
              })
              // 左侧点位
              let circle_from = this.svg.circle(
                this.rectConfig.width, this.rectConfig.height / 2, 5
              )
              circle_from.attr("id", "circle_from_" + node.id)
              circle_from.attr(
                "style", "fill:#2D8CF0; stroke:#9DA3A6; opacity:0.6  "
              )
              _g.add(circle_from)
              _g.attr("id", "g_" + node.id)
              _g.attr(
                "transform", "translate(" + x_value + "," + y_value + ")"
              )
              _g.drag(this.dragMove, this.dragStart, this.dragEnd)
              this.g.add(_g)
            }
            break
        }
      })
      nodes.dataTo.map((node, index) => {
        let _x = parentMatrix.e / parentMatrix.a
        let _y = parentMatrix.f / parentMatrix.a
        let x_value = _x + this.rectConfig.width + this.interval.x // 设置当前节点x-value
        let y_value = _y + index * (this.rectConfig.height + this.interval.y) // 设置当前节点y-value
        switch (node.mode) {
          case "title":
            {
              let rect = _this.svg.rect(
                0, 0, _this.rectConfig.width, _this.rectConfig.height, 0, 0
              )
              rect.attr("style", "fill:transparent;   ")
              // 矩形文字
              let text = _this.svg.text(
                0, (_this.rectConfig.height + 10) / 2, node.label
              )
              text.attr("style", "fill:#2D8CF0;")
              let _g = _this.svg.g()
              _g.add(rect, text)
              _g.attr(
                "transform", "translate(" + x_value + "," + y_value + ")"
              )
            }
            break
          case "header":
            {
              let _g = _this.svg.g()
              let keys = getObjectKeys(node)
              headerColumn = keys.filter((key) => {
                return key !== "mode"
              })
              headerColumn.map((key, keyIndex) => {
                let rect = _this.svg.rect(
                  (keyIndex * _this.rectConfig.width) / headerColumn.length, 0, _this.rectConfig.width / headerColumn.length, _this.rectConfig.height, 0, 0
                )
                rect.attr(
                  "style", "fill:#EDEDED; stroke:#EDEDED; stroke-width:0.5  "
                )
                // 矩形文字
                let text = _this.svg.text(
                  (keyIndex * _this.rectConfig.width) / headerColumn.length +
                  _this.rectConfig.width / headerColumn.length / 2, (_this.rectConfig.height + 10) / 2, node[key]
                )
                text.attr(
                  "style", "dominant-baseline:middle;text-anchor:middle;"
                )
                _g.add(rect, text)
              })
              _g.attr(
                "transform", "translate(" + x_value + "," + y_value + ")"
              )
            }
            break
          default:
            {
              let _g = this.svg.g()
              headerColumn.map((key, keyIndex) => {
                let rect = _this.svg.rect(
                  (keyIndex * _this.rectConfig.width) / headerColumn.length, 0, _this.rectConfig.width / headerColumn.length, _this.rectConfig.height, 0, 0
                )
                rect.attr(
                  "style", "fill:transparent; stroke:#EDEDED; stroke-width:0.5  "
                )
                // 矩形文字
                let text = _this.svg.text(
                  (keyIndex * _this.rectConfig.width) / headerColumn.length +
                  _this.rectConfig.width / headerColumn.length / 2, (_this.rectConfig.height + 10) / 2, node[key]
                )
                text.attr(
                  "style", "dominant-baseline:middle;text-anchor:middle;"
                )
                _g.add(rect, text)
              })

              // 右侧点位
              let circle_to = this.svg.circle(0, this.rectConfig.height / 2, 5)
              circle_to.attr("id", "circle_to_" + node.id)
              circle_to.attr(
                "style", "fill:#2D8CF0; stroke:#9DA3A6; opacity:0.6  "
              )
              _g.add(circle_to)
              _g.attr("id", "g_" + node.id)
              _g.attr(
                "transform", "translate(" + x_value + "," + y_value + ")"
              )
              _g.drag(this.dragMove, this.dragStart, this.dragEnd)
              this.g.add(_g)
            }
            break
        }
      })
    },
    initLinks (linkData) {
      if (!linkData) { return }
      let items = linkData
      items.map((x) => {
        if (x.from !== "" && x.to !== "") {
          let elFrom = Snap("#circle_from_" + x.from)
          let elTo = Snap("#circle_to_" + x.to)
          if (elFrom && elTo) {
            let matrix_from = elFrom.transform().totalMatrix
            let bbox_from = elFrom.getBBox()
            let matrix_to = elTo.transform().totalMatrix
            let bbox_to = elTo.getBBox()

            let moveto = {
              x: matrix_from.e / matrix_from.a + bbox_from.cx,
              y: matrix_from.f / matrix_from.a + bbox_from.cy
            }
            let lineto = {
              x: matrix_to.e / matrix_to.a + bbox_to.cx,
              y: matrix_to.f / matrix_to.a + bbox_to.cy
            }

            let controlPoint1 = {
              x: moveto.x + (lineto.x - moveto.x) / 2,
              y: moveto.y - (lineto.y - moveto.y) / 32
            }

            let controlPoint2 = {
              x: lineto.x - (lineto.x - moveto.x) / 2,
              y: lineto.y + (lineto.y - moveto.y) / 32
            }

            // //折线
            // Snap("#path" + x.from).attr(
            //   "d",
            //   "M" + moveto.x + " " + moveto.y + " L" + lineto.x + " " + lineto.y
            // );

            // 贝塞尔曲线
            let path_value =
              "M" +
              moveto.x +
              " " +
              moveto.y +
              " C" +
              controlPoint1.x +
              " " +
              controlPoint1.y +
              "," +
              controlPoint2.x +
              " " +
              controlPoint2.y +
              "," +
              lineto.x +
              " " +
              lineto.y
            let elPathFromTo = Snap("#path_" + x.from + "_" + x.to)
            if (elPathFromTo) {
              elPathFromTo.attr("d", path_value)
            } else {
              let pathNode = this.svg.path(path_value)
              pathNode.attr("id", "path_" + x.from + "_" + x.to)
              pathNode.attr(
                "style",
                "stroke:#64A4E8;stroke-width:2;fill:none;"
              )
              this.g.add(pathNode)
            }
          }
        }
      })
    },
    dragStart (x, y, event) {
      this.currentElement = event.currentTarget
      let matrix = Snap(this.currentElement).transform().totalMatrix
      this.startX = matrix.e / matrix.a
      this.startY = matrix.f / matrix.a
    },
    dragMove (dx, dy) {
      // 默认移动20像素之后确定为拖拽
      if (Math.abs(dx) > 20) {
        this.dragMoveLine(dx, dy)
      }
    },
    dragEnd () {
      this.getDragEndElement()
      this.currentElement = null
      this.startX = 0
      this.startY = 0
    },
    // click() {
    //   this.$message("单击事件");
    // },
    // doubleClick() {
    //   this.$message("双击事件");
    // },
    // mousedown(event) {
    //   // console.log(event);
    //   if (event.button === 2) {
    //     this.$message("右键点击");
    //   }
    // },
    scaleChange (t) {
      var m = new Snap.Matrix()
      var zoom = 0.08
      if (t === "up") {
        this.scale = this.scale + zoom
      } else {
        this.scale = this.scale - zoom
      }
      m.scale(this.scale)
      this.svg.transform(m)
    },
    // 执行鼠标操作中间路径
    dragMoveLine (dx, dy) {
      let g_id = Snap(this.currentElement).attr("id")
      let id = g_id.replace("g_", "")
      console.log(id)

      let link = {}
      if (id.slice(0, 5) === "from-") {
        link.from = id
      } else if (id.slice(0, 3) === "to-") {
        link.to = id
      }
      this.delLinkData(link)

      let el = null
      let elFrom = Snap("#circle_from_" + id)
      if (elFrom) { el = elFrom }
      let elTo = Snap("#circle_to_" + id)
      if (elTo) { el = elTo }


      if (el) {

        let matrix_from = el.transform().totalMatrix
        let bbox_from = el.getBBox()
        let moveto = {
          x: matrix_from.e / matrix_from.a + bbox_from.cx,
          y: matrix_from.f / matrix_from.a + bbox_from.cy
        }
        let lineto = {
          x: matrix_from.e / matrix_from.a + bbox_from.cx + dx / matrix_from.a,
          y: matrix_from.f / matrix_from.a + bbox_from.cy + dy / matrix_from.a
        }

        let controlPoint1 = {
          x: moveto.x + (lineto.x - moveto.x) / 2,
          y: moveto.y - (lineto.y - moveto.y) / 32
        }

        let controlPoint2 = {
          x: lineto.x - (lineto.x - moveto.x) / 2,
          y: lineto.y + (lineto.y - moveto.y) / 32
        }
        // 贝塞尔曲线
        let path_value =
          "M" +
          moveto.x +
          " " +
          moveto.y +
          " C" +
          controlPoint1.x +
          " " +
          controlPoint1.y +
          "," +
          controlPoint2.x +
          " " +
          controlPoint2.y +
          "," +
          lineto.x +
          " " +
          lineto.y
        let pathFrom = Snap("#path_" + id)
        if (pathFrom) {
          pathFrom.attr("d", path_value)
        } else {
          let pathNode = this.svg.path(path_value)
          pathNode.attr("id", "path_" + id)
          pathNode.attr("style", "stroke:#64A4E8;stroke-width:2; fill:none;")
          this.g.add(pathNode)
        }
      }
    },
    // 鼠标拖动结束获取目标
    getDragEndElement () {
      let g_id = Snap(this.currentElement).attr("id")
      let id = g_id.replace("g_", "")
      let el = Snap("#path_" + id)
      let to = ""
      if (el) {
        let d = el.attr("d")
        let arr = d.split(",")[2].split(" ")
        let point = { x: arr[0], y: arr[1] }

        let from = null
        if (id.slice(0, 5) === "from-") {
          from = id
          to = this.getElementByPoint(this.nodeData.dataTo, point)
        } else if (id.slice(0, 3) === "to-") {
          to = id
          from = this.getElementByPoint(this.nodeData.dataFrom, point)
        }
        el.remove()

        let link = { from, to }
        this.delLinkData(link)
        if (from && to) {
          this.linkData.push(link)
        }
        // console.log("this.linkData", this.linkData);
        this.initLinks(this.linkData)
      }
    },
    // 删除连接线条和连接数据
    delLinkData (link) {
      // let link = { from, to };
      // 删除连线
      this.linkData.map((x, index) => {
        if (x.from === link.from) {
          let el = Snap("#path_" + x.from + "_" + x.to)
          if (el) el.remove()
        }
        if (x.to === link.to) {
          let el = Snap("#path_" + x.from + "_" + x.to)
          if (el) el.remove()
        }
      })
      // 删除数组
      this.linkData.map((x, index) => {
        if (x.from === link.from) {
          this.linkData.splice(index, 1)
        }
      })
      this.linkData.map((x, index) => {
        if (x.to === link.to) {
          this.linkData.splice(index, 1)
        }
      })
    },
    // 根据点位获取目标组
    getElementByPoint (data, point) {
      let id = ""
      let _this = this
      data.map((x) => {
        if (x.id) {
          let el = Snap("#g_" + x.id).transform().totalMatrix
          if (
            point.x >= el.e / el.a &&
            point.x <= el.e / el.a + _this.rectConfig.width &&
            point.y >= el.f / el.a &&
            point.y <= el.f / el.a + _this.rectConfig.height
          ) {
            id = x.id
          }
        }
      })

      return id
    }
  },
  mounted: function () {
    if (this.initData) { this.linkData = [...this.initData] }
    this.initSVG()
  }
};
</script>

<style lang="scss" scoped="scoped">
.snap {
  height: 100%;
  width: 100%;
  min-width: 1200px;
  position: relative;
  overflow: auto;
  // background-color: red;
  .toolbar {
    position: absolute;
    left: 1rem;
    top: 5rem;
    a {
      background-color: #ebeaea;
      margin: 0.5rem;
      border-radius: 5px;
      width: 32px;
      height: 32px;
      line-height: 32px;
      display: block;
      text-align: center;
      cursor: pointer;
    }
  }
  #svg {
    // background-color: #efefef;
    // margin-top: 4rem;
    margin: 2rem 5rem;
    width: calc(100% - 10rem);
    // height: calc(100% - 2rem);
    min-width: 1350px;
  }
}
</style>
